package com.sdy.dcsb.web.controller.inboundservice.util;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sdy.dcsb.biz.constants.MqConstants;
import com.sdy.dcsb.biz.model.*;
import com.sdy.dcsb.biz.model.EsbSystemService;
import com.sdy.dcsb.biz.service.*;
import com.sdy.mq.service.MqProducerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.Timestamp;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import static org.springframework.util.StringUtils.isEmpty;

/**
 * <p>
 * 标准服务请求处理器
 * </p>

 * @author fb
 * @since 2019/10/29
 */
@Slf4j
@Component
public class ProcessorUtil {

    @Autowired
    private EsbInboundMainService esbInboundMainService;
    @Autowired
    private EsbConsumerSystemIpService esbConsumerSystemIpService;
    @Autowired
    private EsbSystemServiceService esbSystemServiceService;
    @Autowired
    private EsbConsumerSystemService esbConsumerSystemService;
    @Autowired
    private EsbServiceConsumerService esbServiceConsumerService;
    @Autowired
    private EsbInboundHttpService esbInboundHttpService;
    @Autowired
    private EsbInboundH5Service esbInboundH5Service;
    @Autowired
    private EsbInboundWebserviceService esbInboundWebserviceService;
    @Autowired
    private EsbAppService esbAppService;
    @Autowired
    private EsbServiceAtomDeployService esbServiceAtomDeployService;
    @Autowired
    private AtomProcessorUtil atomProcessorUtil;
    @Autowired
    private MqProducerService mqProducerService;
    @Autowired
    private FlowUtil flowUtil;

    @Value("${server.port}")
    private String serverPort;

    /**
     * 处理服务请求
     * @param servcode
     * @param servversion
     * @param requestIp
     * @param requestHttpType
     * @param reqLength
     * @param queryString
     * @param contentType
     * @param requestMsg
     * @param reqCharset
     * @param requestTime
     * @param reqType 1:WEBSERVICE,2:HTTP,3:H5
     * @return
     */
    public String doProcess(String servcode, String servversion, String requestIp, String requestHttpType, Integer reqLength,
                            String queryString, String contentType, String requestMsg, String reqCharset, long requestTime,
                            String reqType, String ipAndPort, HashMap<String, String> map, String urladd) {

        String reqStatus = null;
        String responseMsg = "DCSB";
        String reqSystemCode = requestIp;
        Integer serviceId = null;
        long responseTime = 1L;
        // 自动生成http请求唯一流水号
        String reqDcsbId = null;
        if("2".equals(reqType)) {
            reqDcsbId = DcsbIdUtil.getEsbId("DCSB_HTTP_");
        }else if("1".equals(reqType)) {
            reqDcsbId = DcsbIdUtil.getEsbId("DCSB_WEBS_");
        }else if("3".equals(reqType)){
            reqDcsbId = DcsbIdUtil.getEsbId("DCSB_H5_");
        }else {
            reqDcsbId = DcsbIdUtil.getEsbId("DCSB_");
        }
        // 通过服务编码和版本查询服务是否存在
        QueryWrapper<EsbInboundMain> esbInboundMainWrapper = new QueryWrapper<>();
        esbInboundMainWrapper.eq("servcode", servcode);
        esbInboundMainWrapper.eq("servversion", servversion);
        List<EsbInboundMain> esbInboundMainWrapperResult = esbInboundMainService.list(esbInboundMainWrapper);
        String appNmae = "app";
        String domainName = "Domain";
        try{
            if(esbInboundMainWrapperResult.size() == 0) {
                responseMsg = "Request error! Please check serviceCode:" + servcode + " or serviceVersion:" + servversion;
                // 此时访问日志不存
                return responseMsg;
            }
            // 获取用户访问的服务的服务serviceid和超时时间和访问状态
            serviceId = esbInboundMainWrapperResult.get(0).getServiceid();
            String serciveOverTime = esbInboundMainWrapperResult.get(0).getOverTime();
            String serviceStatus = esbInboundMainWrapperResult.get(0).getStatus();
            String serIp = ipAndPort.split(":")[0];
            String serPort = serverPort;
            ipAndPort = serIp + ":" + serPort;
            if("localhost".equals(serIp)) {
                serIp = LocalhostUtil.getIpAddress();
            }
            QueryWrapper<EsbApp> esbAppWrapper = new QueryWrapper();
            esbAppWrapper.eq("ip", serIp);
            esbAppWrapper.eq("httpport", serPort);
            esbAppWrapper.eq("apptype", "2");
            List<EsbApp> esbAppResult = esbAppService.list(esbAppWrapper);
            if(esbAppResult.size() == 0) {
                reqStatus = "3";
                responseMsg = "服务请求失败，未知的请求服务IP: " + serIp + " 和端口: " + serPort;
                throw new Exception(responseMsg);
            }
            appNmae = esbAppResult.get(0).getAppname();
            domainName = esbAppResult.get(0).getDomainname();
            if("0".equals(esbAppResult.get(0).getStatus())) {
                reqStatus = "3";
                responseMsg = "服务所在的服务器不可用!";
                throw new Exception(responseMsg);
            }
            // 通过serviceid查询服务部署信息
            QueryWrapper<EsbServiceAtomDeploy> esbServiceAtomDeployWrapper = new QueryWrapper();
            esbServiceAtomDeployWrapper.eq("serviceid", serviceId);
            esbServiceAtomDeployWrapper.eq("instancename", appNmae);
            esbServiceAtomDeployWrapper.eq("deploytype", 0);
            List<EsbServiceAtomDeploy> esbServiceAtomDeployResult = esbServiceAtomDeployService.list(esbServiceAtomDeployWrapper);
            if(esbServiceAtomDeployResult.size() == 0 || "0".equals(esbServiceAtomDeployResult.get(0).getStatus())) {
                reqStatus = "3";
                responseMsg = "未配置服务部署信息";
                throw new Exception(responseMsg);
            }
            // 通过ip地址查询ip是否在消费者中有备案
            QueryWrapper<EsbConsumerSystemIp> esbConsumerSystemIpWrapper = new QueryWrapper();
            esbConsumerSystemIpWrapper.eq("ip", requestIp);
            List<EsbConsumerSystemIp> esbConsumerSystemIpResult = esbConsumerSystemIpService.list(esbConsumerSystemIpWrapper);
            if(esbConsumerSystemIpResult.size() == 0) {
                reqStatus = "1";
                responseMsg = "未知IP地址信息,IP:" + requestIp;
                throw new Exception(responseMsg);
            }
            Integer reqSystemId = esbConsumerSystemIpResult.get(0).getSystemid();
            Integer requestIpId = esbConsumerSystemIpResult.get(0).getId();
            // 通过系统id查询系统名称和系统编码
            EsbConsumerSystem esbConsumerSystemResult = esbConsumerSystemService.getById(reqSystemId);
            String reqSystemName = esbConsumerSystemResult.getSystemName();
            reqSystemCode = esbConsumerSystemResult.getSystemCode();
            if("0".equals(serviceStatus)) {
                reqStatus = "2";
                responseMsg = "该服务 " + servcode + " 已注销，无法调用！";
                throw new Exception(responseMsg);
            }
            // 通过服务编码和ip的消费者系统id查询此ip是否已加入服务的消费者内
            QueryWrapper<EsbSystemService> esbSystemServiceWrapper = new QueryWrapper();
            esbSystemServiceWrapper.eq("servcode", servcode);
            esbSystemServiceWrapper.eq("system_id", reqSystemId);
            List<EsbSystemService> esbSystemServiceResult = esbSystemServiceService.list(esbSystemServiceWrapper);
            if(esbSystemServiceResult.size() == 0) {
                reqStatus = "1";
                responseMsg = "当前IP:" + requestIp + " ，所在的消费者用户（" + reqSystemName + ")" +
                        "未加入此服务 " + servcode+ " 的消费信息内，无法访问！";
                throw new Exception(responseMsg);
            }
            // 通过ipid和服务编码查询是否有调用权限
            QueryWrapper<EsbServiceConsumer> esbServiceConsumerWrapper = new QueryWrapper();
            esbServiceConsumerWrapper.eq("servcode", servcode);
            esbServiceConsumerWrapper.eq("ipid", requestIpId);
            List<EsbServiceConsumer> esbServiceConsumerResult = esbServiceConsumerService.list(esbServiceConsumerWrapper);
            if(esbServiceConsumerResult.size() == 0) {
                reqStatus = "3";
                responseMsg = "服务(" + servcode +")的消费信息中缺少IP(" + requestIp +"),请联系管理员解决";
                throw new Exception(responseMsg);
            }
            if("0".equals(esbServiceConsumerResult.get(0).getStatus())) {
                reqStatus = "1";
                responseMsg = "该用户（" + reqSystemName + "）的IP:" + requestIp + " 没有调用服务 " + servcode + " 的权限";
                throw new Exception(responseMsg);
            }
            // 通过服务id(serviceid)查询服务的请求方式
            EsbInboundHttp esbInboundHttpResult = esbInboundHttpService.getById(serviceId);
            EsbInboundWebservice esbInboundWebserviceResult = esbInboundWebserviceService.getById(serviceId);
            EsbInboundH5 esbInboundH5Result = esbInboundH5Service.getById(serviceId);
            String targetAddressH5 = "";
            String projectPathH5 = "";
            String typeH5 = "";
            if(!isEmpty(esbInboundHttpResult) && "2".equals(reqType)) {
                String httpType = "";
                if(esbInboundHttpResult.getHttptype() == 1) {
                    httpType = "GET";
                }else {
                    httpType = "POST";
                }
                // 确定用户访问的方式和服务配置的方式一样
                if(httpType.equalsIgnoreCase(requestHttpType)) {
                    // 跳出判断if继续往下执行
                }else {
                    reqStatus = "3";
                    responseMsg = "请用 " + httpType + " 方式调用该服务 "+ servcode;
                    throw new Exception(responseMsg);
                }
            }else if(!isEmpty(esbInboundWebserviceResult) && "1".equals(reqType)) {
                if(!isEmpty(esbInboundWebserviceResult.getEndpoint())) {
                    // 跳出判断if继续往下执行
                }else{
                    reqStatus = "3";
                    responseMsg = "该服务 "+ servcode + " 未配置目标地址";
                    throw new Exception(responseMsg);
                }
            }else if(!isEmpty(esbInboundH5Result) && "3".equals(reqType)) {
                targetAddressH5 = esbInboundH5Result.getTargetaddress();
                projectPathH5 = esbInboundH5Result.getProjectpath();
                typeH5 = esbInboundH5Result.getTarType();
                if(!isEmpty(targetAddressH5)) {
                    // 跳出判断if继续往下执行
                }else {
                    reqStatus = "3";
                    responseMsg = "该服务 "+ servcode + " 未配置目标地址";
                    throw new Exception(responseMsg);
                }
            }else {
                reqStatus = "3";
                responseMsg = "该服务 "+ servcode + " 无对应的请求方式！请配置之后重新访问";
                throw new Exception(responseMsg);
            }
            // 判断请求包大小，超过限制(1M)打回请求（返回错误信息）
            if(reqLength > 1024*1024){
                reqStatus = "3";
                responseMsg = "调用服务 "+ servcode + " 时请求信息过大";
                throw new Exception(responseMsg);
            }
            // 通过系统id和服务编码查询用户每天能够消费的成功访问次数(最大值)
            String reqCountMax = esbSystemServiceResult.get(0).getReqFrequency();
            // 验证用户访问的服务是否超过每日上限（服务器一直运行，若重启则需零点）；
            String name = servcode + "@" + String.valueOf(reqSystemId);
            boolean countVerify = flowUtil.doCheckFrequency(name, reqCountMax);
            if(!countVerify) {
                reqStatus = "4";
                responseMsg = "该用户（" + reqSystemName + "）调用服务 "+ servcode + " 已达到每日最大次数限制（" + reqCountMax + ")";
                throw new Exception(responseMsg);
            }
            // 查询标准服务的原子服务编码(结果是从标准服务中取得)
            String atomServiceCode = esbInboundMainWrapperResult.get(0).getAtomcode();

            if(!"3".equals(reqType)) {
                // 访问原子服务
                responseMsg = atomProcessorUtil.doProcessAtom(atomServiceCode, requestHttpType, queryString, contentType, requestMsg,
                        reqCharset, reqDcsbId, reqType, ipAndPort, map);
                reqStatus = atomProcessorUtil.getAtomReqStatus();
            }else {
                try{
                    responseMsg = getReturnH5(targetAddressH5, projectPathH5, queryString, requestMsg, serciveOverTime, urladd, typeH5);
                    reqStatus = "0";
                }catch (Throwable e) {
                    responseMsg = e.getMessage();
                    reqStatus = "3";
                }
                requestMsg = urladd;
                reqLength = requestMsg.length() + queryString.length();
            }

            responseTime = System.currentTimeMillis();
            long dealTime = Long.parseLong(serciveOverTime);
            if(responseTime >= (requestTime + dealTime)) {
                reqStatus = "3";
                responseMsg = "用户调用服务 " + servcode + " 超时! 超时时间：" + (responseTime-requestTime) + "ms.";
                throw new Exception(responseMsg);
            }
            if(responseMsg.length() > 1024*1024*8) {
                reqStatus = "3";
                responseMsg = "目标接口的返回报文超出系统的8M限制。";
                throw new Exception(responseMsg);
            }

        }catch (Throwable e) {
            if("DCSB".equals(responseMsg)) {
                reqStatus = "3";
                responseMsg = "服务器异常，请联系管理员!";
            }
        }
        if(1L == responseTime) {
            responseTime = System.currentTimeMillis();
        }

        // 日志保存实体类
        Timestamp reqTime = new Timestamp(requestTime);
        Timestamp respTime = new Timestamp(responseTime);

        JSONObject esbLogJson = new JSONObject();
        esbLogJson.put("domainname", domainName);
        esbLogJson.put("appname", appNmae);
        esbLogJson.put("requestip", requestIp);
        esbLogJson.put("reqid", reqDcsbId);
        esbLogJson.put("expid", serviceId);
        esbLogJson.put("status", reqStatus);
        esbLogJson.put("reqlength", String.valueOf(reqLength));
        esbLogJson.put("resplength", String.valueOf(responseMsg.length()));
        esbLogJson.put("request_time", reqTime);
        esbLogJson.put("response_time", respTime);
        esbLogJson.put("system_code", reqSystemCode);
        try{
            mqProducerService.send(MqConstants.Tags.ESB_LOG, esbLogJson.toJSONString());
        }catch (Exception e) {
            log.info(e.getMessage());
            log.info("时间：" + reqTime + "保存服务日志到mq失败");
        }

        JSONObject esbLogReqJson = new JSONObject();
        esbLogReqJson.put("reqid", reqDcsbId);
        esbLogReqJson.put("request_time", reqTime);
        esbLogReqJson.put("response_time", respTime);
        esbLogReqJson.put("system_code", reqSystemCode);
        esbLogReqJson.put("expid", serviceId);
        esbLogReqJson.put("servcode", servcode);
        esbLogReqJson.put("insoapmsg", requestMsg);
        esbLogReqJson.put("outsoapmsg", responseMsg);
        esbLogReqJson.put("version", servversion);
        esbLogReqJson.put("querystring", queryString);
        esbLogReqJson.put("reqtype", reqType);

        if(esbLogReqJson.toJSONString().length() > 1024*1024*4) {
            esbLogReqJson.put("outsoapmsg", "");
            Integer num = 1;
            String response = responseMsg;
            while(response.length() > 1023*1024*4) {
                String outsoapmsg = response.substring(0, 1023*1024*4);
                JSONObject esbLogReqSplitJson = new JSONObject();
                esbLogReqSplitJson.put("reqid", reqDcsbId);
                esbLogReqSplitJson.put("outsoapmsg", outsoapmsg);
                esbLogReqSplitJson.put("num", num);
                try{
                    mqProducerService.send(MqConstants.Tags.ESB_LOG_REQ_SPLIT, esbLogReqSplitJson.toJSONString());
                }catch (Exception e) {
                    log.info(e.getMessage());
                    log.info("时间：" + reqTime + "保存服务拆分报文到mq失败：流水号" + reqDcsbId + ",num = " + num);
                }
                response = response.substring(1023*1024*4);
                num++;
            }
            JSONObject esbLogReqSplitJson = new JSONObject();
            esbLogReqSplitJson.put("reqid", reqDcsbId);
            esbLogReqSplitJson.put("outsoapmsg", response);
            esbLogReqSplitJson.put("num", num);
            esbLogReqSplitJson.put("size", num);
            try{
                mqProducerService.send(MqConstants.Tags.ESB_LOG_REQ_SPLIT, esbLogReqSplitJson.toJSONString());
            }catch (Exception e) {
                log.info(e.getMessage());
                log.info("时间：" + reqTime + "保存服务拆分报文到mq失败：流水号" + reqDcsbId + ",num = " + num);
            }
        }
        try{
            mqProducerService.send(MqConstants.Tags.ESB_LOG_REQ, esbLogReqJson.toJSONString());
        }catch (Exception e) {
            log.info(e.getMessage());
            log.info("时间：" + reqTime + "保存服务报文到mq失败");
        }

        return responseMsg;
    }

    private String getReturnH5(String targetAddressH5, String projectPathH5, String queryString, String requestMsg, String serciveOverTime, String urladd,
                               String typeH5) throws Exception{
        String responseMsg = null;
        String urlStr = targetAddressH5;
        if(!isEmpty(urladd)) {
            if("H".equals(typeH5)) {
                urlStr = projectPathH5 + urladd;
            }else {
                urlStr = targetAddressH5 + urladd;
            }
        }
        if(!isEmpty(queryString)) {
            urlStr = urlStr + "?" + queryString;
        }
        try{
            URL url = new URL(urlStr);
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setReadTimeout(Integer.parseInt(serciveOverTime));
            connection.setRequestMethod("GET");
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(true);
            connection.setRequestProperty("Content-Type", "text/html;charset=utf-8");
            connection.connect();
            DataOutputStream dataout = new DataOutputStream(connection.getOutputStream());
            dataout.write(requestMsg.toString().getBytes());
            dataout.flush();
            dataout.close();
            if(!"H".equals(typeH5)) {
                InputStream inputStream = connection.getInputStream();
                byte[] data = readInputStream(inputStream);
//                inputStream.read(data);
                inputStream.close();
                Base64.Encoder encoder = Base64.getEncoder();
                responseMsg = encoder.encodeToString(data);
            }else {
                BufferedReader bf = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
                StringBuilder sb = new StringBuilder();
                String line;
                while((line = bf.readLine()) != null) {
                    sb.append(line).append(System.getProperty("line.separator"));
                }
                responseMsg = sb.toString();
                bf.close();
            }
            connection.disconnect();
        }catch (Exception e) {
            responseMsg = "请求h5出错，错误原因：访问目标地址数据出错。";
            throw new Exception(responseMsg);
        }
        if(responseMsg == null) {
            responseMsg = "DCSB";
        }
        return responseMsg;
    }

    private static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[2048];
        int len;
        while((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        return outStream.toByteArray();
    }

}
